/**
  ******************************************************************************
  * @file    Project/Bsp/glcd.c 
  * @author  zlf
  * @version V1.0.0
  * @date    2016-04-06
  * @brief   glcd.c module
  ******************************************************************************
  */
  
/* Includes ------------------------------------------------------------------*/
	#include "glcd.h"
	#include "../oled/oled.h"

/* Private typedef -----------------------------------------------------------*/

/* Private define ------------------------------------------------------------*/

/* Private macro -------------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/

/* Private variables ---------------------------------------------------------*/

GlcdPositionStructType Glcd_Position_Cur;

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  This function to Set Glcd data line to output.
  * @param  None
  * @retval None
  */
void Glcd_Data_Set_Output(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//initialise lcd data level to output
	GPIO_InitStructure.GPIO_Pin = GLCD_DATA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GLCD_DATA_GPIO, &GPIO_InitStructure);
}

/**
  * @brief  This function to Set Glcd data line to input.
  * @param  None
  * @retval None
  */
void Glcd_Data_Set_Input(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//initialise lcd data level to input
	GPIO_InitStructure.GPIO_Pin = GLCD_DATA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GLCD_DATA_GPIO, &GPIO_InitStructure);
}

/**
  * @brief  This function to initialise the hardware of glcd.
  * @param  None
  * @retval None
  */
void Glcd_Init_HW(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	//initialise lcd control port to output
	GPIO_InitStructure.GPIO_Pin = GLCD_CTRL_RS | GLCD_CTRL_RW | 
								  GLCD_CTRL_E;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GLCD_CTRL_GPIO1, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin =  GLCD_CTRL_CS0 | GLCD_CTRL_CS1 |
		 						   GLCD_CTRL_RESET;
	GPIO_Init(GLCD_CTRL_GPIO2, &GPIO_InitStructure);
	
	//initialise lcd data level to output
	Glcd_Data_Set_Output();

	//initialise LCD control port
	CLR_LCD_E;
	CLR_LCD_CS0;
	CLR_LCD_CS1;
	CLR_LCD_RESET;
	SET_LCD_CMD;
	SET_LCD_WRITE;
}

/**
  * @brief  This function to initialise the hardware of glcd.
  * @param  None
  * @retval None
  */
void Glcd_Controller_Select( u8 controller )
{
	switch( controller )
	{
		case GLCD_LEFT_CONTROLLER:
			CLR_LCD_CS1;
			SET_LCD_CS0;
			break;

		case GLCD_RIGHT_CONTROLLER:
			CLR_LCD_CS0;
			SET_LCD_CS1;
			break;

		default:
			break;
	}
}

/**
  * @brief  This function to check if the glcd is busy or not.
  * @param  controller, the half of glcd that will be selected
  * @param  GLCD_LEFT_CONTROLLER, left half of glcd;
  * @param  GLCD_RIGHT_CONTROLLER, right half of glcd.
  * @retval None
  */
void Glcd_Busy_Wait( u8 controller )
{
	Glcd_Controller_Select(controller);
	SET_LCD_CMD;
	//set data as input
	Glcd_Data_Set_Input();	
	SET_LCD_READ;
	SET_LCD_E;
	//delay E fall time more than 25ns
	delay_us(E_CYCLE_TIME);
	while( LCD_DATA_READ & GLCD_STATUS_BUSY )
	{
		CLR_LCD_E;
		delay_us(E_CYCLE_TIME);
		SET_LCD_E;
		delay_us(E_CYCLE_TIME);
	}
	CLR_LCD_E;
	SET_LCD_WRITE;
	
	//set data as output
	Glcd_Data_Set_Output();

}

/**
  * @brief  This function to send a command to graphic LCD.
  * @param  controller, the half of glcd that will be selected
  * @param  data, the command set   
  * @retval None
  */
void Glcd_Control_Write(u8 controller, u8 data )
{
	Glcd_Busy_Wait(controller);
	SET_LCD_CMD;
	SET_LCD_WRITE;

	//set data as output
	Glcd_Data_Set_Output();

	LCD_DATA_WRITE( data );
	SET_LCD_E;
	delay_us(E_CYCLE_TIME);
	CLR_LCD_E;
}

/**
  * @brief  This function to read a command from graphic LCD.
  * @param  controller, the half of glcd that will be selected
  * @retval None
  */
u8 Glcd_Control_Read(u8 controller)
{
	u8 Status;

	Glcd_Busy_Wait(controller);
	SET_LCD_CMD;
	SET_LCD_READ;

	//set data as input
	Glcd_Data_Set_Input();

	SET_LCD_E;
	delay_us(E_CYCLE_TIME);
	Status = LCD_DATA_READ;
	CLR_LCD_E;

	return Status;
}

/**
  * @brief  This function to write a data to graphic LCD.
  * @param  data, the data to be written.
  * @retval None
  */
void Glcd_Data_Write(u8 data)
{
	u8 controller = Glcd_Position_Cur.LcdXAddress/GLCD_CONTROLLER_PIXELS;

	Glcd_Busy_Wait( controller );
	SET_LCD_DATA;
	SET_LCD_WRITE;
	
	//set data as output
	Glcd_Data_Set_Output();

	LCD_DATA_WRITE( data );

	SET_LCD_E;
	delay_us(E_CYCLE_TIME);
	CLR_LCD_E;

	Glcd_Position_Cur.LcdXAddress++;
	if( Glcd_Position_Cur.LcdXAddress >= GLCD_XPIXELS )
	{
		Glcd_Position_Cur.LcdYAddress++;
		if( Glcd_Position_Cur.LcdYAddress >= GLCD_MAX_PAGE )
		{
			Glcd_Position_Cur.LcdYAddress = 0;
		}
		Glcd_Set_Y_Address(Glcd_Position_Cur.LcdYAddress);
		Glcd_Set_X_Address(0);
	}
}

/**
  * @brief  This function to read a data to graphic LCD.
  * @param  None
  * @retval data, the data to be read.
  */
u8 Glcd_Data_Read(void)
{
	u8 Data = 0x00;
	u8 controller = Glcd_Position_Cur.LcdXAddress/GLCD_CONTROLLER_PIXELS;
		
	Glcd_Busy_Wait( controller );
	SET_LCD_DATA;
	SET_LCD_READ;

	//set data as input
	Glcd_Data_Set_Input();

	SET_LCD_E;
	delay_us(E_CYCLE_TIME);
	Data = LCD_DATA_READ;
	CLR_LCD_E;

	Glcd_Position_Cur.LcdXAddress++;
	if( Glcd_Position_Cur.LcdXAddress >= GLCD_XPIXELS )
	{
		Glcd_Position_Cur.LcdYAddress++;
		if( Glcd_Position_Cur.LcdYAddress >= GLCD_MAX_PAGE )
		{
			Glcd_Position_Cur.LcdYAddress = 0;
		}
		Glcd_Set_Y_Address(Glcd_Position_Cur.LcdYAddress);
		Glcd_Set_X_Address(0);
	}

	return Data;
}

/**
  * @brief  This function to set X address on graphic LCD.
  * @param  xAddr, X address.
  * @retval None
  */
void Glcd_Set_X_Address(u8 xAddr)
{
	Glcd_Position_Cur.LcdXAddress = xAddr;
	//clear all the column address on all controllers
	Glcd_Control_Write(GLCD_LEFT_CONTROLLER,  GLCD_SET_COLUMN | 0x00);
	Glcd_Control_Write(GLCD_RIGHT_CONTROLLER, GLCD_SET_COLUMN | 0x00);
	//set the column address on the destination controller
	Glcd_Control_Write( (Glcd_Position_Cur.LcdXAddress/GLCD_CONTROLLER_PIXELS), 
						GLCD_SET_COLUMN | (Glcd_Position_Cur.LcdXAddress%GLCD_CONTROLLER_PIXELS) );
}

/**
  * @brief  This function to set Y address on graphic LCD.
  * @param  yAddr, Y address, 0 ~ 7
  * @retval None
  */
void Glcd_Set_Y_Address(u8 yAddr)
{
	Glcd_Position_Cur.LcdYAddress = yAddr;
	//set both controller to destination
	Glcd_Control_Write(GLCD_LEFT_CONTROLLER, GLCD_SET_PAGE | yAddr);
	Glcd_Control_Write(GLCD_RIGHT_CONTROLLER, GLCD_SET_PAGE | yAddr);
}

/**
  * @brief  This function to set X address and Y address on graphic LCD.
  * @param  xAddr, X address, 0~128
  * @param  yAddr, Y address, 0~3  
  * @retval None
  */
/*
void Glcd_Set_XY_Address(u8 xAddr, u8 yAddr)
{
	Glcd_Set_X_Address(xAddr);
	Glcd_Set_Y_Address(yAddr);
}
*/

void Glcd_Set_XY_Address(u8 xAddr, u8 yAddr)
{
	OLED_Set_XY_Address(xAddr,yAddr);
}

/**
  * @brief  This function to clear the screen of the graphic LCD.
  * @param  None 
  * @retval None
  */
/*void Glcd_Clear_Screen(void)
{
	u8 Page, Column;

	for(Page = 0; Page < (GLCD_YPIXELS>>3); Page++ )
	{
		Glcd_Set_XY_Address(0, Page);
		for(Column = 0; Column < GLCD_XPIXELS; Column++)
		{
			Glcd_Data_Write(0x00);
		}
	}
}*/

void Glcd_Clear_Screen(void)
{
	OLED_Clear();
}

/**
  * @brief  This function to clear the downhalf screen of the graphic LCD.
  * @param  None 
  * @retval None
  */
/*
void Glcd_Clear_Downhalf_Screen(void)
{
	Glcd_Clear_Line(0, 4, 127);
	Glcd_Clear_Line(0, 6, 127);	
}
*/

void Glcd_Clear_Downhalf_Screen(void)
{
	Glcd_Clear_Line(MENU_ORIGIN, MENU_LINE_THREE, MENU_PIXAL);
	Glcd_Clear_Line(MENU_ORIGIN, MENU_LINE_FOUR, MENU_PIXAL);	
}
/**
  * @brief  This function to return the upper, left of the graphic LCD.
  * @param  None 
  * @retval None
  */
void Glcd_Home(void)
{
	Glcd_Set_XY_Address( 0, 0);
}

/**
  * @brief  This function to start the first display line.
  * @param  None 
  * @retval None
  */
void Glcd_Start_Line( u8 Start )
{
	Glcd_Control_Write(GLCD_LEFT_CONTROLLER, GLCD_START_LINE | Start );
	Glcd_Control_Write(GLCD_RIGHT_CONTROLLER, GLCD_START_LINE | Start );
}

/**
  * @brief  This function to initialise the graphic LCD.
  * @param  None 
  * @retval None
  */
void Glcd_Init(void)
{
	Glcd_Init_HW();

	//Power on and reset
	CLR_LCD_RESET;
	delay_us(RESET_CYCLE_TIME);	
	SET_LCD_RESET;
	
	Glcd_Start_Line(0);
	
	Glcd_Control_Write(GLCD_LEFT_CONTROLLER, GLCD_ON_CTRL | GLCD_ON_DISPLAY );
	Glcd_Control_Write(GLCD_RIGHT_CONTROLLER, GLCD_ON_CTRL | GLCD_ON_DISPLAY );

	//Clear the lcd
	Glcd_Clear_Screen();
	//Initialise the positions
	Glcd_Home();
	
}

/**
  * @brief  This function to deinitialise the graphic LCD.
  * @param  None 
  * @retval None
  */
void Glcd_DeInit(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	//Deinitialise lcd data level to output
	GPIO_InitStructure.GPIO_Pin = GLCD_DATA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GLCD_DATA_GPIO, &GPIO_InitStructure);

	//Deinitialise lcd data level to output
	GPIO_InitStructure.GPIO_Pin = GLCD_CTRL_RW|GLCD_CTRL_RS|
								  GLCD_CTRL_E;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GLCD_CTRL_GPIO1, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GLCD_CTRL_CS0|GLCD_CTRL_CS1|
 								  GLCD_CTRL_RESET;
	GPIO_Init(GLCD_CTRL_GPIO2, &GPIO_InitStructure);	
}


